Skip to content

fix(parse): treat empty/typeless schema as "any" in transform_schema#1691

Open
WalkingDreams798 wants to merge 1 commit into
anthropics:mainfrom
WalkingDreams798:fix/transform-schema-untyped-any
Open

fix(parse): treat empty/typeless schema as "any" in transform_schema#1691
WalkingDreams798 wants to merge 1 commit into
anthropics:mainfrom
WalkingDreams798:fix/transform-schema-untyped-any

Conversation

@WalkingDreams798

Copy link
Copy Markdown

What

transform_schema (the structured-output schema transformer used by messages.parse / beta.messages.parse) raised

ValueError: Schema must have a 'type', 'anyOf', 'oneOf', or 'allOf' field.

for any sub-schema that pydantic emits as an empty {} — namely an Any-typed field, or the items of an untyped list / List[Any].

Repro

import pydantic
from typing import Any, List
import anthropic

class Result(pydantic.BaseModel):
    summary: str
    tags: List[Any]   # also: a bare `list`, or any `Any`-typed field

client = anthropic.Anthropic(api_key="sk-ant-...")
client.beta.messages.parse(
    model="claude-sonnet-4-5",
    max_tokens=100,
    messages=[{"role": "user", "content": "hi"}],
    output_format=Result,
)
# -> ValueError: Schema must have a 'type', 'anyOf', 'oneOf', or 'allOf' field.

The error is raised at request-build time, before any API call. Notably, an untyped dict / Dict[str, Any] field does not trip it (it keeps type: object), so the failure is specific to untyped lists and Any fields — surprising and easy to hit.

Root cause: in the array branch, items: {} recurses into transform_schema({}), and a schema with no type/anyOf/oneOf/allOf hit the raise. The same happens for an Any object property.

Change

An empty {} is a valid, unconstrained ("any") JSON Schema. Instead of raising when there is no type and no combinator, leave the sub-schema untyped (permissive). One-line behavior change in src/anthropic/lib/_parse/_transform.py.

Tests

Added to tests/lib/_parse/test_transform.py:

  • empty {} schema
  • untyped array items ({"type": "array", "items": {}})
  • an Any object property
  • end-to-end pydantic model with List[Any] + Any field (regression for the original crash)

Verification

  • uv run pytest tests/lib/_parse → 26 passed
  • uv run ruff check → all checks passed
  • uv run pyright (strict) on changed files → 0 errors, 0 warnings

`transform_schema` raised `ValueError("Schema must have a 'type', 'anyOf',
'oneOf', or 'allOf' field.")` for any sub-schema that pydantic emits as an empty
`{}` — i.e. an `Any`-typed field, or the items of an untyped `list` / `List[Any]`.

This made structured outputs unusable for very common models:

    class Result(BaseModel):
        summary: str
        tags: list[Any]   # or a bare `list`, or any `Any`-typed field

    client.beta.messages.parse(..., output_format=Result)
    # ValueError raised at request-build time, before the API is called

An empty `{}` is a valid, unconstrained ("any") JSON Schema. Instead of raising
when there is no `type`/combinator, leave the sub-schema untyped. Added tests for
the empty schema, untyped array items, an `Any` object property, and an
end-to-end pydantic model regression.
@WalkingDreams798 WalkingDreams798 requested a review from a team as a code owner June 19, 2026 18:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant